package ssl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import rsa.RsaFunktionen;

public class SecureSocketClient {
	
	protected static File PublicKey = new File("src//ssl//clientData//cPublicKey.asc");
	protected static File PrivateKey = new File("src//ssl//clientData//cPrivateKey.asc");
	
	protected static File sPublicKey = new File("src//ssl//clientData//sPublicKey.asc");
	protected static File encPw = new File("src//ssl//clientData//encPw.secure");
	
	protected static File generatedPw = new File("src//ssl//clientData//GeneratedPw.txt");
	
	protected static String encryptedPwPuffer;
	protected static String decryptedPw;
	
	
	protected static byte encryptedPw[];
	
	public SecureSocketClient(String username, String host) {
		// TODO Auto-generated constructor stub
	}
	
    public static void main(String[] args) 
    {
    
    	String Name = "Dummy";
    	
    	System.setProperty("javax.net.ssl.trustStore","myKeystore"); // Truststore File in den Umgebungsvariablen festlegen
		System.setProperty("javax.net.ssl.trustStorePassword", "123456"); // Truststore Passwort in den Umgebungsvariablen festlegen
        
    	try {

    		RsaFunktionen rsa = new RsaFunktionen();
			KeyPair key = rsa.schluesselErstellen();
			RSAPublicKey rsaPublicKey=(RSAPublicKey) key.getPublic();
			RSAPrivateKey rsaPrivateKey=(RSAPrivateKey) key.getPrivate();
			
			String[] cPubKeyString = generateStringRSAPublicKey(rsaPublicKey);
			exportRSAPrivateKey(rsaPrivateKey, PrivateKey);
    		
            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); // Erzeugt eine SSLSocketFactory mit Standartkonfiguration
            SSLSocket socket = (SSLSocket) factory.createSocket("localhost", 5432); // SSLSocket erstellen (und zu Server verbinden)
           
            
           
            sendName(Name, socket); //TODO: Benutzername nur Dummy -> Ben�tigt den Benutzernamen, mit dem man Eingabe get�tigt hat
            
            
            socket = (SSLSocket) factory.createSocket("localhost", 5432);
            sendPublicKey(cPubKeyString, socket);
            
            socket = (SSLSocket) factory.createSocket("localhost", 5432);
            receivePublicKey(sPublicKey, socket); 
            
            socket = (SSLSocket) factory.createSocket("localhost", 5432);
            
            
            String xform = "RSA";
            
            receivePw(encPw, socket);
            fileToByteArray();
            byte[] dec = decrypt(encryptedPw, key.getPrivate(), xform);
            
            String decryptedPw = new String(dec);
            System.out.println(decryptedPw);
            storeGeneratedPw(Name, decryptedPw);
            
        } catch (Exception ex) {
        	ex.printStackTrace();
        	System.out.println("Die Verbindung zum Server ist abgebrochen!");
        }
    }

    public static void storeGeneratedPw(String Name, String pw) //and delete temporary encryptedPW Data 
    { 
		boolean append = false;
		try {
			if(generatedPw.exists())	
				append = true;
			else
				append = false;
				
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream( generatedPw, append)));
			bw.write(Name + " \n" +  pw);
			bw.newLine();
			bw.flush();
			
			if (encPw.exists()) 
				encPw.delete();
			
		} catch( IOException ex ) {
		    System.out.println( ex );
		  }
	}
    
	
	public static void sendName(String Name, SSLSocket socket)
			throws IOException {
		OutputStream rawOut = socket.getOutputStream();
		PrintWriter out = new PrintWriter(new OutputStreamWriter(rawOut));
		out.println(Name);
		out.flush();
		socket.close();
	}

	public static byte[] receivePassword(SSLSocket socket) throws IOException {
		InputStream rawIn = socket.getInputStream( );
		BufferedReader in = new BufferedReader(new InputStreamReader(rawIn));
		encryptedPwPuffer = in.readLine();
		System.out.println("encryptedPw String:"+ encryptedPwPuffer);
		encryptedPw = encryptedPwPuffer.getBytes();
		System.out.println("encryptedPw Bytes"+ encryptedPw);
		
		socket.close();
		
		return encryptedPw;
	}
	
public static void fileToByteArray() throws IOException {
		
	encryptedPw = new byte[(int) encPw.length()];
		FileInputStream fileInputStream = new FileInputStream(encPw);
		fileInputStream.read(encryptedPw);
		fileInputStream.close();
		
}

	public static void receivePw(File file, SSLSocket socket)
								throws FileNotFoundException, IOException {
	
		FileOutputStream fos = new FileOutputStream(file);
	
		DataInputStream is = new DataInputStream(socket.getInputStream()); //ankommende Datei lesen
	
		int j = 0;
		while ((j = is.read()) != -1)
		{
		    fos.write(j);                
		}
		fos.flush();
		
		is.close();
		fos.close();
	}
	
	
	  public static byte[] decrypt(byte[] inpBytes, PrivateKey key,
		String xForm){
		Cipher cipher;
		try {
			cipher = Cipher.getInstance(xForm);
			cipher.init(Cipher.DECRYPT_MODE, key);
			return cipher.doFinal(inpBytes);
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	  }

	public static void receivePublicKey(File tmpFile, SSLSocket socket)
			throws FileNotFoundException, IOException {
		FileOutputStream fos = new FileOutputStream(tmpFile);
		DataInputStream is = new DataInputStream(socket.getInputStream()); //ankommende Datei lesen
		
		int j = 0;
		while ((j = is.read()) != -1)
		{
		    fos.write(j);                
		}
		fos.flush();
       
		
		is.close();
		fos.close();
		System.out.println("sPublicKey erhalten!");
	}

	public static void sendPublicKey(String[] cPubKeyString, SSLSocket socket)
			throws IOException, FileNotFoundException {
		
		ObjectOutputStream dos = new ObjectOutputStream(socket.getOutputStream());
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(dos));
 
		bw.write(cPubKeyString[0] + '\n');
		bw.write(cPubKeyString[1] + '\n');
		
		bw.flush();
		bw.close();
		dos.close();
		
		System.out.println("cPublicKey zum Server transferiert!");
	}
	
	
	public static String[] generateStringRSAPublicKey(RSAPublicKey rsaPublicKey)
    {
       
		String mod=rsaPublicKey.getModulus().toString(16);
        String exp=rsaPublicKey.getPublicExponent().toString(16);
        
        String[] retval = {mod, exp};
        return retval;
    }
	
	public static void exportRSAPrivateKey(RSAPrivateKey rsaPrivateKey, File file) throws Exception
    {
        PrintStream out=new PrintStream(new FileOutputStream(file));
        String mod=rsaPrivateKey.getModulus().toString(16);
        String exp=rsaPrivateKey.getPrivateExponent().toString(16);
        out.print(mod+"\n"+exp);
        out.close();
    }
	
	
}

